package com.lry.jvm.instructions.refrences;

import com.lry.jvm.instructions.base.BytecodeReader;
import com.lry.jvm.instructions.base.NoOperandsInstruction;
import com.lry.jvm.rtda.Frame;
import com.lry.jvm.rtda.JObject;
import com.lry.jvm.rtda.OperandStack;
import com.lry.jvm.rtda.heap.ClassRef;
import com.lry.jvm.rtda.heap.JClass;
import com.lry.jvm.rtda.heap.RuntimeConstantPool;
import com.lry.jvm.util.Util;

import java.util.Arrays;

//多维数组，一定是引用类型数组
//multianewarray指令的第一个操作数是个uint16索引，
//        通过这个 索引可以从运行时常量池中找到一个类符号引用，
//        解析这个引用就 可以得到多维数组类。
//        第二个操作数是个uint8整数，表示数组维 度
public class multianewarray extends NoOperandsInstruction {

    private short index;
    private byte dimensions;
    @Override
    public byte getOpCode() {
        return (byte) 0xc5;
    }

    @Override
    public void fetchOperands(BytecodeReader reader) {
        index = reader.read16();//常量池索引，根据他判断数组类型
        dimensions = reader.read8();//数组维度
    }

//    multianewarray指令还需要从操作数栈中弹出n个整数，
//    分别代 表每一个维度的数组长度。
//    Execute（）方法根据数组类、数组维度和
//    各个维度的数组长度创建多维数组
    @Override
    public void execute(Frame frame) throws Exception {
        RuntimeConstantPool rcp = frame.getMethod().getJClass().getConstantPool();
        ClassRef classRef = (ClassRef) rcp.getConstant(index);
        JClass arrClass = classRef.resolvedClass();

        OperandStack stack = frame.getOperandStack();
        int[] counts = popAndCheckCounts(stack,dimensions);
        JObject arr = newMultiDimensionArray(counts,arrClass);
        stack.pushRef(arr);
    }

    private JObject newMultiDimensionArray(int[] counts, JClass arrClass) {
        int count = counts[0];
        JObject arr = arrClass.newArray(count);

        if(counts.length>1){
            JObject[] refs = arr.toRefs();
            for(int i=0;i<refs.length;i++){
                refs[i] = newMultiDimensionArray(Arrays.stream(counts).skip(1).toArray(),arrClass.componentClass());
            }
        }
        return arr;
    }

    //在anewarray指令中，解析类符号引用后得 到的是数组元素的类，
    //而multianewarray解析出来的直接就是数组类
    private int[] popAndCheckCounts(OperandStack stack, byte dimensions) {
//        从操作数栈中弹出n个int值，并且确保 它们都大于等于0。
//        如果其中任何一个小于0，则抛出 NegativeArraySizeException异常
        int[] counts = new int[dimensions];
        for(int i=dimensions-1;i>=0;i--){
            counts[i] = stack.popInt();
            if(counts[i]<0){
                Util.panic("java.lang.NegativeArraySizeException");
            }
        }
        return counts;
    }
}
